﻿using System;
using Microsoft.Xna.Framework;
using Phoenix.Xna.Framework.Components.Cameras;
using Phoenix.Xna.Framework.Graphics;

namespace Phoenix.Xna.Framework.Components.Terrain
{
    public class TerrainNode
    {
        public const int MaxChildren = 4;
        public const int SidesNumber = 5;
        public const int VerticesNumber = 9;
        private static int CurrentIndice = 0;
        private BoundingBox _boundingBoxNorthWestChild;
        private BoundingBox _boundingBoxNorthEastChild;
        private BoundingBox _boundingBoxSouthWestChild;
        private BoundingBox _boundingBoxSouthEastChild;
        private float[] _realToInterpolatedVertexHeight;
        private float _dotNorthWestChild;
        private float _dotSouthWestChild;
        private float _dotSouthEastChild;
        private float _dotNorthEastChild;
        private TerrainPrimitive _primitive;

        public NodeContent EnabledVertices { get; private set; }
        public bool IsVisible { get; internal set; }
        public NodeChild ChildPosition { get; internal set; }
        public TerrainVertex[] Vertices { get; private set; }
        public TerrainVertex NorthWestVertex
        {
            get { return Vertices[(int)NodeVertex.NorthWest]; }
            set { Vertices[(int)NodeVertex.NorthWest] = value; }
        }
        public TerrainVertex NorthEastVertex
        {
            get { return Vertices[(int)NodeVertex.NorthEast]; }
            set { Vertices[(int)NodeVertex.NorthEast] = value; }
        }
        public TerrainVertex SouthWestVertex
        {
            get { return Vertices[(int)NodeVertex.SouthWest]; }
            set { Vertices[(int)NodeVertex.SouthWest] = value; }
        }
        public TerrainVertex SouthEastVertex
        {
            get { return Vertices[(int)NodeVertex.SouthEast]; }
            set { Vertices[(int)NodeVertex.SouthEast] = value; }
        }
        public TerrainVertex EastVertex
        {
            get { return Vertices[(int)NodeVertex.East]; }
            set { Vertices[(int)NodeVertex.East] = value; }
        }
        public TerrainVertex WestVertex
        {
            get { return Vertices[(int)NodeVertex.West]; }
            set { Vertices[(int)NodeVertex.West] = value; }
        }
        public TerrainVertex NorthVertex
        {
            get { return Vertices[(int)NodeVertex.North]; }
            set { Vertices[(int)NodeVertex.North] = value; }
        }
        public TerrainVertex SouthVertex
        {
            get { return Vertices[(int)NodeVertex.South]; }
            set { Vertices[(int)NodeVertex.South] = value; }
        }
        public TerrainVertex CenterVertex
        {
            get { return Vertices[(int)NodeVertex.Center]; }
            set { Vertices[(int)NodeVertex.Center] = value; }
        }
        public float Size { get { return Tree.GetNodeSizeAtLevel(Depth); } }
        public bool IsLeaf { get { return (((int)EnabledVertices >> 9) == 0); } }
        public bool HasNoEdge { get { return (!NorthVertex.Enabled && !SouthVertex.Enabled && !EastVertex.Enabled && !WestVertex.Enabled); } }
        public ProgressiveTerrain Tree { get; set; }
        public TerrainNode Parent { get; set; }
        public TerrainNode[] Neighbors { get; set; }
        public TerrainNode NorthWestChild
        {
            get { return Children[(int)NodeChild.NorthWest]; }
            set { Children[(int)NodeChild.NorthWest] = value; }
        }
        public TerrainNode NorthEastChild
        {
            get { return Children[(int)NodeChild.NorthEast]; }
            set { Children[(int)NodeChild.NorthEast] = value; }
        }
        public TerrainNode SouthWestChild
        {
            get { return Children[(int)NodeChild.SouthWest]; }
            set { Children[(int)NodeChild.SouthWest] = value; }
        }
        public TerrainNode SouthEastChild
        {
            get { return Children[(int)NodeChild.SouthEast]; }
            set { Children[(int)NodeChild.SouthEast] = value; }
        }
        public TerrainNode EastNeighbor
        {
            get { return Neighbors[(int)NodeSide.East]; }
            set { Neighbors[(int)NodeSide.East] = value; }
        }
        public TerrainNode WestNeighbor
        {
            get { return Neighbors[(int)NodeSide.West]; }
            set { Neighbors[(int)NodeSide.West] = value; }
        }
        public TerrainNode NorthNeighbor
        {
            get { return Neighbors[(int)NodeSide.North]; }
            set { Neighbors[(int)NodeSide.North] = value; }
        }
        public TerrainNode SouthNeighbor
        {
            get { return Neighbors[(int)NodeSide.South]; }
            set { Neighbors[(int)NodeSide.South] = value; }
        }
        public TerrainNode[] Children { get; set; }
        public byte Depth { get; set; }
        public Vector2 Position { get; set; }
        public bool HasChildren
        {
            get
            {
                for (int i = 0; i < MaxChildren; i++)
                {
                    if (Children[i] != null)
                    {
                        return true;
                    }
                }

                return false;
            }
        }

        public TerrainNode(TerrainNode parent, NodeChild position)
        {
            Children = new TerrainNode[MaxChildren];
            Neighbors = new TerrainNode[MaxChildren];
            Vertices = new TerrainVertex[TerrainNode.VerticesNumber];
            _realToInterpolatedVertexHeight = new float[TerrainNode.SidesNumber];
            Parent = parent;
            ChildPosition = position;

            if (parent == null)
            {
                Depth = 0;
            }
            else
            {
                Depth = Convert.ToByte(parent.Depth + 1);
                Tree = parent.Tree;
            }
        }

        public static Vector3 GetBoundingBoxClosestPointToPoint(BoundingBox boundingBox, Vector3 point)
        {
            float x;
            float y;
            float z;

            if (point.X > boundingBox.Max.X)
            {
                x = boundingBox.Max.X;
            }
            else if (point.X < boundingBox.Min.X)
            {
                x = boundingBox.Min.X;
            }
            else
            {
                x = point.X;
            }

            if (point.Y > boundingBox.Max.Y)
            {
                y = boundingBox.Max.Y;
            }
            else if (point.Y < boundingBox.Min.Y)
            {
                y = boundingBox.Min.Y;
            }
            else
            {
                y = point.Y;
            }

            if (point.Z > boundingBox.Max.Z)
            {
                z = boundingBox.Max.Z;
            }
            else if (point.Z < boundingBox.Min.Z)
            {
                z = boundingBox.Min.Z;
            }
            else
            {
                z = point.Z;
            }

            return new Vector3(x, y, z);
        }

        public void Initialize()
        {
            float size = Size - 1;
            float x = Position.X;
            float z = Position.Y;
            float xWhole = x + size;
            float zWhole = z + size;
            float zHalf = z + size / 2;
            float xHalf = x + size / 2;
            float heightXZWhole = Tree.GetHeight(x, zWhole);
            float heightXWholeZWhole = Tree.GetHeight(xWhole, zWhole);
            float heightXWholeZ = Tree.GetHeight(xWhole, z); ;
            float heightXZ = Tree.GetHeight(x, z);
            Vector3 normal = new Plane(new Vector3(x, heightXZWhole, zWhole), new Vector3(xWhole, heightXWholeZ, z), new Vector3(x, heightXZ, z)).Normal *
                new Plane(new Vector3(x, heightXZWhole, zWhole), new Vector3(xWhole, heightXWholeZWhole, zWhole), new Vector3(xWhole, heightXWholeZ, z)).Normal;
            normal.Normalize();

            // Compute the four edges of the current square.
            if (Parent != null)
            {
                // If there is a parent node use its verticies.
                switch (ChildPosition)
                {
                    case NodeChild.NorthWest:
                        NorthWestVertex = Parent.NorthWestVertex;
                        NorthEastVertex = Parent.NorthVertex;
                        SouthEastVertex = Parent.CenterVertex;
                        SouthWestVertex = Parent.WestVertex;
                        break;
                    case NodeChild.NorthEast:
                        NorthWestVertex = Parent.NorthVertex;
                        NorthEastVertex = Parent.NorthEastVertex;
                        SouthWestVertex = Parent.CenterVertex;
                        SouthEastVertex = Parent.EastVertex;
                        break;
                    case NodeChild.SouthWest:
                        NorthWestVertex = Parent.WestVertex;
                        NorthEastVertex = Parent.CenterVertex;
                        SouthWestVertex = Parent.SouthWestVertex;
                        SouthEastVertex = Parent.SouthVertex;
                        break;
                    case NodeChild.SouthEast:
                        NorthWestVertex = Parent.CenterVertex;
                        NorthEastVertex = Parent.EastVertex;
                        SouthWestVertex = Parent.SouthVertex;
                        SouthEastVertex = Parent.SouthEastVertex;
                        break;
                    default:
                        break;
                }

                CenterVertex = new TerrainVertex(new VertexPositionNormal(new Vector3(xHalf, Tree.GetHeight(xHalf, zHalf), zHalf), normal));
                WestVertex = (WestNeighbor != null ? WestNeighbor.EastVertex : new TerrainVertex(new VertexPositionNormal(new Vector3(x, Tree.GetHeight(x, zHalf), zHalf), normal)));
                NorthVertex = (NorthNeighbor != null ? NorthNeighbor.SouthVertex : new TerrainVertex(new VertexPositionNormal(new Vector3(xHalf, Tree.GetHeight(xHalf, zWhole), zWhole), normal)));
                EastVertex = (EastNeighbor != null ? EastNeighbor.WestVertex : new TerrainVertex(new VertexPositionNormal(new Vector3(xWhole, Tree.GetHeight(xWhole, zHalf), zHalf), normal)));
                SouthVertex = (SouthNeighbor != null ? SouthNeighbor.NorthVertex : new TerrainVertex(new VertexPositionNormal(new Vector3(xHalf, Tree.GetHeight(xHalf, z), z), normal)));
            }
            // This node is the root node.
            else
            {
                Vector3 northWest = new Vector3(x, heightXZWhole, zWhole);
                Vector3 northEast = new Vector3(xWhole, heightXWholeZWhole, zWhole);
                Vector3 southEast = new Vector3(xWhole, heightXWholeZ, z);
                Vector3 southWest = new Vector3(x, heightXZ, z);
                Vector3 west = new Vector3(x, Tree.GetHeight(x, zHalf), zHalf);
                Vector3 north = new Vector3(xHalf, Tree.GetHeight(xHalf, zWhole), zWhole);
                Vector3 east = new Vector3(xWhole, Tree.GetHeight(xWhole, zHalf), zHalf);
                Vector3 south = new Vector3(xHalf, Tree.GetHeight(xHalf, z), z);
                NorthWestVertex = new TerrainVertex(new VertexPositionNormal(northWest, normal));
                NorthEastVertex = new TerrainVertex(new VertexPositionNormal(northEast, normal));
                SouthEastVertex = new TerrainVertex(new VertexPositionNormal(southEast, normal));
                SouthWestVertex = new TerrainVertex(new VertexPositionNormal(southWest, normal));
                CenterVertex = new TerrainVertex(new VertexPositionNormal(new Vector3(xHalf, Tree.GetHeight(xHalf, zHalf), zHalf), normal));
                WestVertex = new TerrainVertex(new VertexPositionNormal(west, normal));
                NorthVertex = new TerrainVertex(new VertexPositionNormal(north, normal));
                EastVertex = new TerrainVertex(new VertexPositionNormal(east, normal));
                SouthVertex = new TerrainVertex(new VertexPositionNormal(south, normal));
            }

            // Enable the verticies.
            EnableVertex(NodeContent.NorthWestVertex, NodeVertex.NorthWest);
            EnableVertex(NodeContent.NorthEastVertex, NodeVertex.NorthEast);
            EnableVertex(NodeContent.SouthEastVertex, NodeVertex.SouthEast);
            EnableVertex(NodeContent.SouthWestVertex, NodeVertex.SouthWest);

            // Interpolate all four sides and the center. Here can easily deduce the position x and z because we know the size of the
            // current square and its location. To get the height we have to interpolate from the two neighboring egdes. 
            float centerHeight = (float)(0.25 * (NorthEastVertex.Value.Position.Y + NorthWestVertex.Value.Position.Y + SouthWestVertex.Value.Position.Y + SouthEastVertex.Value.Position.Y));
            float eastSideHeight = (float)(0.5 * (SouthEastVertex.Value.Position.Y + NorthEastVertex.Value.Position.Y));
            float northSideHeight = (float)(0.5 * (NorthEastVertex.Value.Position.Y + NorthWestVertex.Value.Position.Y));
            float westSideHeight = (float)(0.5 * (NorthWestVertex.Value.Position.Y + SouthWestVertex.Value.Position.Y));
            float southSideHeight = (float)(0.5 * (SouthWestVertex.Value.Position.Y + SouthEastVertex.Value.Position.Y));
            _realToInterpolatedVertexHeight[(int)NodeSide.Center] = Math.Abs(centerHeight - CenterVertex.Value.Position.Y);
            _realToInterpolatedVertexHeight[(int)NodeSide.East] = Math.Abs(eastSideHeight - EastVertex.Value.Position.Y);
            _realToInterpolatedVertexHeight[(int)NodeSide.North] = Math.Abs(northSideHeight - NorthVertex.Value.Position.Y);
            _realToInterpolatedVertexHeight[(int)NodeSide.West] = Math.Abs(westSideHeight - WestVertex.Value.Position.Y);
            _realToInterpolatedVertexHeight[(int)NodeSide.South] = Math.Abs(southSideHeight - SouthVertex.Value.Position.Y);

            // Create the bounding boxes for the sides.
            _boundingBoxNorthWestChild = BoundingBox.CreateFromPoints(new Vector3[] { CenterVertex.Value.Position, NorthWestVertex.Value.Position, WestVertex.Value.Position, NorthVertex.Value.Position });
            _boundingBoxNorthEastChild = BoundingBox.CreateFromPoints(new Vector3[] { CenterVertex.Value.Position, NorthEastVertex.Value.Position, EastVertex.Value.Position, NorthVertex.Value.Position });
            _boundingBoxSouthWestChild = BoundingBox.CreateFromPoints(new Vector3[] { CenterVertex.Value.Position, SouthWestVertex.Value.Position, WestVertex.Value.Position, SouthVertex.Value.Position });
            _boundingBoxSouthEastChild = BoundingBox.CreateFromPoints(new Vector3[] { CenterVertex.Value.Position, SouthEastVertex.Value.Position, EastVertex.Value.Position, SouthVertex.Value.Position });

            // Find the dot-product of the normals.
            Vector3 childNormal = Vector3.Multiply(
                new Plane(NorthVertex.Value.Position, NorthEastVertex.Value.Position, EastVertex.Value.Position).Normal,
                new Plane(NorthVertex.Value.Position, EastVertex.Value.Position, CenterVertex.Value.Position).Normal);
            normal.Normalize();
            _dotNorthEastChild = 1 - Vector3.Dot(childNormal, normal);
            childNormal = Vector3.Multiply(
                new Plane(NorthWestVertex.Value.Position, NorthVertex.Value.Position, CenterVertex.Value.Position).Normal,
                new Plane(NorthWestVertex.Value.Position, CenterVertex.Value.Position, WestVertex.Value.Position).Normal);
            childNormal.Normalize();
            _dotNorthWestChild = 1 - Vector3.Dot(childNormal, normal);
            childNormal = Vector3.Multiply(
                new Plane(CenterVertex.Value.Position, EastVertex.Value.Position, SouthEastVertex.Value.Position).Normal,
                new Plane(CenterVertex.Value.Position, SouthEastVertex.Value.Position, SouthVertex.Value.Position).Normal);
            childNormal.Normalize();
            _dotSouthEastChild = 1 - Vector3.Dot(childNormal, normal);
            childNormal = Vector3.Multiply(
                new Plane(WestVertex.Value.Position, CenterVertex.Value.Position, SouthVertex.Value.Position).Normal,
                new Plane(WestVertex.Value.Position, SouthVertex.Value.Position, SouthWestVertex.Value.Position).Normal);
            childNormal.Normalize();
            _dotSouthWestChild = 1 - Vector3.Dot(childNormal, normal);
        }

        public TerrainVertex SetVertex(float x, float z, Vector3 normal)
        {
            VertexPositionNormal v0 = new VertexPositionNormal();
            v0.Position = new Vector3(x, Tree.GetHeight(x, z), z);
            v0.Normal = normal;
            return new TerrainVertex(v0);
        }

        public Vector3 GetNormal(Vector3 nw, Vector3 ne, Vector3 sw, Vector3 se)
        {
            Vector3 normal = new Plane(nw, ne, se).Normal * new Plane(nw, se, sw).Normal;
            normal.Normalize();
            return normal;
        }

        public Vector3 GetNormal(float left, float right, float top, float bottom)
        {
            Vector3 nwp = new Vector3(left, Tree.GetHeight(left, top), top);
            Vector3 nep = new Vector3(right, Tree.GetHeight(right, top), top);
            Vector3 swp = new Vector3(left, Tree.GetHeight(left, bottom), bottom);
            Vector3 sep = new Vector3(right, Tree.GetHeight(right, bottom), bottom);
            return GetNormal(nwp, nep, swp, sep);
        }

        public Vector3 GetNormal(TerrainVertex nw, TerrainVertex ne, TerrainVertex sw, TerrainVertex se)
        {
            Vector3 nwp = nw.Value.Position;
            Vector3 nep = ne.Value.Position;
            Vector3 swp = sw.Value.Position;
            Vector3 sep = se.Value.Position;
            return GetNormal(nwp, nep, swp, sep);
        }

        public void Update()
        {
            if (Depth < ProgressiveTerrain.MaxDepth)
            {
                CheckVertexAt(NodeVertex.East, NodeContent.EastVertex, NodeSide.East);
                CheckVertexAt(NodeVertex.South, NodeContent.SouthVertex, NodeSide.South);
                CheckChildAt(NodeChild.NorthWest, NodeContent.NorthWestChild, _dotNorthWestChild, _boundingBoxNorthWestChild);
                CheckChildAt(NodeChild.NorthEast, NodeContent.NorthEastChild, _dotNorthEastChild, _boundingBoxNorthEastChild);
                CheckChildAt(NodeChild.SouthEast, NodeContent.SouthEastChild, _dotSouthEastChild, _boundingBoxSouthEastChild);
                CheckChildAt(NodeChild.SouthWest, NodeContent.SouthWestChild, _dotSouthWestChild, _boundingBoxSouthWestChild);

                for (int i = 0; i < TerrainNode.MaxChildren; i++)
                {
                    if ((Children[i] != null) && (!Children[i].IsVisible))
                    {
                        Children[i].Update();
                    }
                }
            }
        }

        public void EnableVertex(NodeContent flag, NodeVertex position)
        {
            EnableVertex(flag);
            Vertices[(int)position].AddReferenceTo(this);
        }

        public void EnableVertex(NodeContent flag)
        {
            EnabledVertices |= flag;

            if (((int)EnabledVertices >> 5) != 0)
            {
                EnabledVertices |= NodeContent.CenterVertex;
                CenterVertex.AddReferenceTo(this);
            }

            // If an edge is enabled you must enable its neighbor to share the edge vertices.
            if (((int)flag >> 5) != 0 && (Parent != null))
            {
                switch (flag)
                {
                    #region West Vertex
                    case NodeContent.WestVertex:
                        if (WestNeighbor == null)
                        {
                            if (ChildPosition == NodeChild.NorthWest)
                            {
                                if (Parent.WestNeighbor != null)
                                {
                                    if (Parent.WestNeighbor.NorthEastChild == null)
                                    {
                                        Parent.WestNeighbor.AddChildAt(NodeChild.NorthEast, NodeContent.NorthEastChild);
                                    }
                                }
                            }
                            else if (ChildPosition == NodeChild.SouthWest)
                            {
                                if (Parent.WestNeighbor != null)
                                {
                                    if (Parent.WestNeighbor.SouthEastChild == null)
                                    {
                                        Parent.WestNeighbor.AddChildAt(NodeChild.SouthEast, NodeContent.SouthEastChild);
                                    }
                                }
                            }
                            else if (ChildPosition == NodeChild.SouthEast)
                            {
                                if (Parent.SouthWestChild == null)
                                {
                                    Parent.AddChildAt(NodeChild.SouthWest, NodeContent.SouthWestChild);
                                }
                            }
                            else if (ChildPosition == NodeChild.NorthEast)
                            {
                                if (Parent.NorthWestChild == null)
                                {
                                    Parent.AddChildAt(NodeChild.NorthWest, NodeContent.NorthWestChild);
                                }
                            }
                        }
                        break;
                    #endregion
                    #region North Vertex
                    case NodeContent.NorthVertex:
                        if (NorthNeighbor == null)
                        {
                            if (ChildPosition == NodeChild.NorthWest)
                            {
                                if (Parent.NorthNeighbor != null)
                                {
                                    if (Parent.NorthNeighbor.SouthWestChild == null)
                                    {
                                        Parent.NorthNeighbor.AddChildAt(NodeChild.SouthWest, NodeContent.SouthWestChild);
                                    }
                                }
                            }
                            else if (ChildPosition == NodeChild.NorthEast)
                            {
                                if (Parent.NorthNeighbor != null)
                                {
                                    if (Parent.NorthNeighbor.SouthEastChild == null)
                                    {
                                        Parent.NorthNeighbor.AddChildAt(NodeChild.SouthEast, NodeContent.SouthEastChild);
                                    }
                                }
                            }
                            else if (ChildPosition == NodeChild.SouthEast)
                            {
                                if (Parent.NorthEastChild == null)
                                {
                                    Parent.AddChildAt(NodeChild.NorthEast, NodeContent.NorthEastChild);
                                }
                            }
                            else if (ChildPosition == NodeChild.SouthWest)
                            {
                                if (Parent.NorthWestChild == null)
                                {
                                    Parent.AddChildAt(NodeChild.NorthWest, NodeContent.NorthWestChild);
                                }
                            }
                        }
                        break;
                    #endregion
                    #region East Vertex
                    case NodeContent.EastVertex:
                        if (EastNeighbor == null)
                        {
                            if (ChildPosition == NodeChild.NorthEast)
                            {
                                if (Parent.EastNeighbor != null)
                                {
                                    if (Parent.EastNeighbor.NorthWestChild == null)
                                    {
                                        Parent.EastNeighbor.AddChildAt(NodeChild.NorthWest, NodeContent.NorthWestChild);
                                    }
                                }
                            }
                            else if (ChildPosition == NodeChild.SouthEast)
                            {
                                if (Parent.EastNeighbor != null)
                                {
                                    if (Parent.EastNeighbor.SouthWestChild == null)
                                    {
                                        Parent.EastNeighbor.AddChildAt(NodeChild.SouthWest, NodeContent.SouthWestChild);
                                    }
                                }
                            }
                            else if (ChildPosition == NodeChild.NorthWest)
                            {
                                if (Parent.NorthEastChild == null)
                                {
                                    Parent.AddChildAt(NodeChild.NorthEast, NodeContent.NorthEastChild);
                                }
                            }
                            else if (ChildPosition == NodeChild.SouthWest)
                            {
                                if (Parent.SouthEastChild == null)
                                {
                                    Parent.AddChildAt(NodeChild.SouthEast, NodeContent.SouthEastChild);
                                }
                            }
                        }
                        break;
                    #endregion
                    #region South Vertex
                    case NodeContent.SouthVertex:
                        if (SouthNeighbor == null)
                        {
                            if (ChildPosition == NodeChild.SouthEast)
                            {
                                if (Parent.SouthNeighbor != null)
                                {
                                    if (Parent.SouthNeighbor.NorthEastChild == null)
                                    {
                                        Parent.SouthNeighbor.AddChildAt(NodeChild.NorthEast, NodeContent.NorthEastChild);
                                    }
                                }
                            }
                            else if (ChildPosition == NodeChild.SouthWest)
                            {
                                if (Parent.SouthNeighbor != null)
                                {
                                    if (Parent.SouthNeighbor.NorthWestChild == null)
                                    {
                                        Parent.SouthNeighbor.AddChildAt(NodeChild.NorthWest, NodeContent.NorthWestChild);
                                    }
                                }
                            }
                            else if (ChildPosition == NodeChild.NorthEast)
                            {
                                if (Parent.SouthEastChild == null)
                                {
                                    Parent.AddChildAt(NodeChild.SouthEast, NodeContent.SouthEastChild);
                                }
                            }
                            else if (ChildPosition == NodeChild.NorthWest)
                            {
                                if (Parent.SouthWestChild == null)
                                {
                                    Parent.AddChildAt(NodeChild.SouthWest, NodeContent.SouthWestChild);
                                }
                            }
                        }
                        break;
                    #endregion
                    default:
                        break;
                }
            }
        }

        public void DisableVertex(NodeContent flag, NodeVertex position)
        {
            DisableVertex(flag);
            Vertices[(int)position].RemoveReferenceFrom(this);
        }

        public void DisableVertex(NodeContent flag)
        {
            EnabledVertices &= ~flag;

            if (((int)EnabledVertices >> 5) == 0)
            {
                EnabledVertices &= ~NodeContent.CenterVertex;
                CenterVertex.RemoveReferenceFrom(this);
            }
        }

        public void BuildNode()
        {
            if (!HasNoEdge)
            {
                if (NorthWestChild == null)
                {
                    if (SouthWestChild != null)
                    {
                        AddVertex(NodeVertex.Center);
                        AddVertex(NodeVertex.West);
                        AddVertex(NodeVertex.NorthWest);
                    }
                    else if (WestVertex.Enabled)
                    {
                        AddVertex(NodeVertex.Center);
                        AddVertex(NodeVertex.West);
                        AddVertex(NodeVertex.NorthWest);
                    }

                    AddVertex(NodeVertex.Center);
                    AddVertex(NodeVertex.NorthWest);

                    if (NorthEastChild != null)
                    {
                        AddVertex(NodeVertex.North);
                    }
                }

                if (NorthEastChild == null)
                {
                    if (NorthWestChild != null)
                    {
                        AddVertex(NodeVertex.Center);
                        AddVertex(NodeVertex.North);
                    }
                    else if (NorthVertex.Enabled)
                    {
                        AddVertex(NodeVertex.North);
                        AddVertex(NodeVertex.Center);
                        AddVertex(NodeVertex.North);
                    }

                    AddVertex(NodeVertex.NorthEast);
                    AddVertex(NodeVertex.Center);
                    AddVertex(NodeVertex.NorthEast);

                    if (SouthEastChild != null)
                    {
                        AddVertex(NodeVertex.East);
                    }
                }

                if (SouthEastChild == null)
                {
                    if (NorthEastChild != null)
                    {
                        AddVertex(NodeVertex.Center);
                        AddVertex(NodeVertex.East);
                    }
                    else if (EastVertex.Enabled)
                    {
                        AddVertex(NodeVertex.East);
                        AddVertex(NodeVertex.Center);
                        AddVertex(NodeVertex.East);
                    }

                    AddVertex(NodeVertex.SouthEast);
                    AddVertex(NodeVertex.Center);
                    AddVertex(NodeVertex.SouthEast);

                    if (SouthWestChild != null)
                    {
                        AddVertex(NodeVertex.South);
                    }
                }

                if (SouthWestChild == null)
                {
                    if (SouthEastChild != null)
                    {
                        AddVertex(NodeVertex.Center);
                        AddVertex(NodeVertex.South);
                    }
                    else if (SouthVertex.Enabled)
                    {
                        AddVertex(NodeVertex.South);
                        AddVertex(NodeVertex.Center);
                        AddVertex(NodeVertex.South);
                    }

                    AddVertex(NodeVertex.SouthWest);
                    AddVertex(NodeVertex.Center);
                    AddVertex(NodeVertex.SouthWest);

                    if (NorthWestChild != null)
                    {
                        AddVertex(NodeVertex.West);
                    }
                    else
                    {
                        if (WestVertex.Enabled)
                        {
                            AddVertex(NodeVertex.West);
                        }
                        else
                        {
                            AddVertex(NodeVertex.NorthWest);
                        }
                    }
                }
            }
            else
            {
                AddVertex(NodeVertex.NorthWest);
                AddVertex(NodeVertex.NorthEast);
                AddVertex(NodeVertex.SouthEast);
                AddVertex(NodeVertex.NorthWest);
                AddVertex(NodeVertex.SouthEast);
                AddVertex(NodeVertex.SouthWest);
            }

            for (int i = 0; i < TerrainNode.MaxChildren; i++)
            {
                if (Children[i] != null)
                {
                    Children[i].BuildNode();
                }
            }
        }

        public bool TestVisibility(Vector3 vertexPosition, NodeSide side)
        {
            float lengthToTest = _realToInterpolatedVertexHeight[(int)side];
            float distanceCameraToPoint = Vector3.Distance(vertexPosition, Camera.ActiveCamera.Position);
            return lengthToTest * Tree.VertexDetail > distanceCameraToPoint;
        }

        private void InitializeNeighbors()
        {
            if (Parent == null)
            {
                return;
            }

            switch (ChildPosition)
            {
                #region NorthWest
                case NodeChild.NorthWest:
                    EastNeighbor = Parent.NorthEastChild;

                    if (Parent.NorthEastChild != null)
                    {
                        Parent.NorthEastChild.WestNeighbor = this;
                    }

                    SouthNeighbor = Parent.SouthWestChild;

                    if (Parent.SouthWestChild != null)
                    {
                        Parent.SouthWestChild.NorthNeighbor = this;
                    }

                    if (Parent.WestNeighbor != null)
                    {
                        WestNeighbor = Parent.WestNeighbor.NorthEastChild;

                        if (Parent.WestNeighbor.NorthEastChild != null)
                        {
                            Parent.WestNeighbor.NorthEastChild.EastNeighbor = this;
                        }
                    }

                    if (Parent.NorthNeighbor != null)
                    {
                        NorthNeighbor = Parent.NorthNeighbor.SouthWestChild;

                        if (Parent.NorthNeighbor.SouthWestChild != null)
                        {
                            Parent.NorthNeighbor.SouthWestChild.SouthNeighbor = this;
                        }
                    }
                    break;
                #endregion
                #region NorthEast
                case NodeChild.NorthEast:
                    WestNeighbor = Parent.NorthWestChild;

                    if (Parent.NorthWestChild != null)
                    {
                        Parent.NorthWestChild.EastNeighbor = this;
                    }

                    SouthNeighbor = Parent.SouthEastChild;

                    if (Parent.SouthEastChild != null)
                    {
                        Parent.SouthEastChild.NorthNeighbor = this;
                    }

                    if (Parent.EastNeighbor != null)
                    {
                        EastNeighbor = Parent.EastNeighbor.NorthWestChild;

                        if (Parent.EastNeighbor.NorthWestChild != null)
                        {
                            Parent.EastNeighbor.NorthWestChild.WestNeighbor = this;
                        }
                    }

                    if (Parent.NorthNeighbor != null)
                    {
                        NorthNeighbor = Parent.NorthNeighbor.SouthEastChild;

                        if (Parent.NorthNeighbor.SouthEastChild != null)
                        {
                            Parent.NorthNeighbor.SouthEastChild.SouthNeighbor = this;
                        }
                    }
                    break;
                #endregion
                #region SouthWest
                case NodeChild.SouthWest:
                    EastNeighbor = Parent.SouthEastChild;

                    if (Parent.SouthEastChild != null)
                    {
                        Parent.SouthEastChild.WestNeighbor = this;
                    }

                    NorthNeighbor = Parent.NorthWestChild;

                    if (Parent.NorthWestChild != null)
                    {
                        Parent.NorthWestChild.SouthNeighbor = this;
                    }

                    if (Parent.WestNeighbor != null)
                    {
                        WestNeighbor = Parent.WestNeighbor.SouthEastChild;

                        if (Parent.WestNeighbor.SouthEastChild != null)
                        {
                            Parent.WestNeighbor.SouthEastChild.EastNeighbor = this;
                        }
                    }

                    if (Parent.SouthNeighbor != null)
                    {
                        SouthNeighbor = Parent.SouthNeighbor.NorthWestChild;

                        if (Parent.SouthNeighbor.NorthWestChild != null)
                        {
                            Parent.SouthNeighbor.NorthWestChild.NorthNeighbor = this;
                        }
                    }
                    break;
                #endregion
                #region SouthEast
                case NodeChild.SouthEast:
                    WestNeighbor = Parent.SouthWestChild;

                    if (Parent.SouthWestChild != null)
                    {
                        Parent.SouthWestChild.EastNeighbor = this;
                    }

                    NorthNeighbor = Parent.NorthEastChild;

                    if (Parent.NorthEastChild != null)
                    {
                        Parent.NorthEastChild.SouthNeighbor = this;
                    }

                    if (Parent.EastNeighbor != null)
                    {
                        EastNeighbor = Parent.EastNeighbor.SouthWestChild;

                        if (Parent.EastNeighbor.SouthWestChild != null)
                        {
                            Parent.EastNeighbor.SouthWestChild.WestNeighbor = this;
                        }
                    }

                    if (Parent.SouthNeighbor != null)
                    {
                        SouthNeighbor = Parent.SouthNeighbor.NorthEastChild;

                        if (Parent.SouthNeighbor.NorthEastChild != null)
                        {
                            Parent.SouthNeighbor.NorthEastChild.NorthNeighbor = this;
                        }
                    }
                    break;
                #endregion
                default:
                    break;
            }
        }

        private bool IsEdgeUsed(NodeVertex position)
        {
            return Vertices[(int)position].Enabled;
        }

        private bool IsEnabled(NodeContent flag)
        {
            return ((EnabledVertices & flag) != NodeContent.None);
        }

        private bool IsDisabled(NodeContent flag)
        {
            return ((EnabledVertices & flag) == NodeContent.None);
        }

        private void AddVertex(NodeVertex position)
        {
            VertexPositionNormal vertexValue = Vertices[(int)position].Value;
            TerrainVertex vertex = Vertices[(int)position];

            if (Tree.ProcessIterationCount != vertex.LastIteration)
            {
                vertex.BufferIndex = Tree.Vertices.Count;
                Tree.Vertices.Add(vertexValue);
                vertex.LastIteration = Tree.ProcessIterationCount;
            }

            Tree.Indices.Add(vertex.BufferIndex);

            if (CurrentIndice == 0)
            {
                _primitive = new TerrainPrimitive(0, 0, 0);
                _primitive.Indice1 = vertex.BufferIndex;
                CurrentIndice++;
            }
            else if (CurrentIndice == 1)
            {
                _primitive.Indice2 = vertex.BufferIndex;
                CurrentIndice++;
            }
            else if (CurrentIndice == 2)
            {
                _primitive.Indice3 = vertex.BufferIndex;
                CurrentIndice = 0;
                _primitive.SetNormal(Tree.Vertices);
            }
        }

        private void CheckVertexAt(NodeVertex position, NodeContent flag, NodeSide side)
        {
            if (IsDisabled(flag) && TestVisibility(Vertices[(int)position].Value.Position, side))
            {
                EnableVertex(flag, position);
            }
            else if (IsEnabled(flag) && !TestVisibility(Vertices[(int)position].Value.Position, side))
            {
                DisableVertex(flag, position);
            }
        }

        private void CheckChildAt(NodeChild position, NodeContent flag, float dotproduct, BoundingBox childBox)
        {
            if (IsDisabled(flag) && TestChildVisibility(dotproduct, childBox))
            {
                AddChildAt(position, flag);
            }
            else if (Children[(int)position] != null &&
                IsEnabled(flag) &&
                Children[(int)position].IsLeaf &&
                Children[(int)position].HasNoEdge &&
                !TestChildVisibility(dotproduct, childBox))
            {
                RemoveChildAt(position, flag);
            }
        }

        private void RemoveChildAt(NodeChild position, NodeContent flag)
        {
            DisableVertex(flag);
            TerrainNode node = Children[(int)position];

            switch (position)
            {
                case NodeChild.NorthWest:
                    DisableVertex(NodeContent.NorthVertex, NodeVertex.North);
                    DisableVertex(NodeContent.WestVertex, NodeVertex.West);
                    break;
                case NodeChild.NorthEast:
                    DisableVertex(NodeContent.NorthVertex, NodeVertex.North);
                    DisableVertex(NodeContent.EastVertex, NodeVertex.East);
                    break;
                case NodeChild.SouthWest:
                    DisableVertex(NodeContent.SouthVertex, NodeVertex.South);
                    DisableVertex(NodeContent.WestVertex, NodeVertex.West);
                    break;
                default:
                    DisableVertex(NodeContent.SouthVertex, NodeVertex.South);
                    DisableVertex(NodeContent.EastVertex, NodeVertex.East);
                    break;
            }

            Children[(int)position] = null;
            InitializeNeighbors();
            node.InitializeNeighbors();
            node.Dispose();
        }

        private void AddChildAt(NodeChild position, NodeContent flag)
        {
            EnableVertex(flag);
            TerrainNode node = new TerrainNode(this, position);
            Children[(int)position] = node;
            float size = node.Size - 1;

            switch (position)
            {
                case NodeChild.NorthWest:
                    node.Position = Position + new Vector2(0, size);
                    break;
                case NodeChild.NorthEast:
                    node.Position = Position + new Vector2(size, size);
                    break;
                case NodeChild.SouthWest:
                    node.Position = Position + new Vector2(0, 0);
                    break;
                default:
                    node.Position = Position + new Vector2(size, 0);
                    break;
            }

            node.InitializeNeighbors();
            InitializeNeighbors();
            node.Initialize();

            switch (position)
            {
                case NodeChild.NorthWest:
                    EnableVertex(NodeContent.NorthVertex, NodeVertex.North);
                    EnableVertex(NodeContent.WestVertex, NodeVertex.West);
                    break;
                case NodeChild.NorthEast:
                    EnableVertex(NodeContent.NorthVertex, NodeVertex.North);
                    EnableVertex(NodeContent.EastVertex, NodeVertex.East);
                    break;
                case NodeChild.SouthWest:
                    EnableVertex(NodeContent.SouthVertex, NodeVertex.South);
                    EnableVertex(NodeContent.WestVertex, NodeVertex.West);
                    break;
                default:
                    EnableVertex(NodeContent.SouthVertex, NodeVertex.South);
                    EnableVertex(NodeContent.EastVertex, NodeVertex.East);
                    break;
            }
        }

        private bool TestChildVisibility(float dotproduct, BoundingBox boundingBox)
        {
            if (Depth < Tree.MinNodeDepth)
            {
                return true;
            }

            float distanceCameraToPoint = Vector3.Distance(GetBoundingBoxClosestPointToPoint(boundingBox, Camera.ActiveCamera.Position), Camera.ActiveCamera.Position);
            return ((distanceCameraToPoint - Tree.NearDetailThreshold) / Tree.FarDetailThreshold) < (dotproduct);
        }

        private void Dispose(bool disposing)
        {
            if (!IsDisposed && disposing)
            {
                for (int i = 0; i < MaxChildren; i++)
                {
                    if (Children[i] != null)
                    {
                        if (Children[i] is IDisposable)
                        {
                            IDisposable child = Children[i] as IDisposable;
                            child.Dispose();
                        }
                    }
                }

                for (int i = 0; i < Vertices.Length; i++)
                {
                    Vertices[i].RemoveReferenceFrom(this);
                }

                for (int i = 0; i < TerrainNode.MaxChildren; i++)
                {
                    if (Children[i] != null)
                    {
                        Children[i].Dispose();
                        Children[i] = null;
                    }
                }

                IsDisposed = true;
            }
        }

        #region IDisposableEx Members

        public bool IsDisposed { get; protected set; }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
